/**
 * Kernel Monkeys
 * Andrew Boguk
 * Sergiy Kulyk
 * Nicholas Swartzendruber
 */

/*
 *  FILE   : queue.c
 *  AUTHOR : Jeffrey Hunter
 *  WEB    : http://www.iDevelopment.info
 *  NOTES  : Implement all functions required
 *           for a Queue data structure.
 *
 *  Obtained from: http://www.idevelopment.info/data/Programming/data_structures/c/Queue/Queue.shtml
 *  Edited by: Nicholas Swartzendruber
 */

#include "queue.h"
#include <stdlib.h>

/**
 * @return A value that evaluates to true if the
 * passed queue is empty.
 */
int queue_IsEmpty(Queue Q) {
  return Q->Size == 0;
}

/**
 * @return A value that evaluates to true if the
 * passed queue is full.
 */
int queue_IsFull(Queue Q) {
  return Q->Size == Q->Capacity;
}

/**
 * @param MaxElements The max number of elements that this queue can
 * hold.
 * @return A new queue that may store values in a FIFO order.
 */
Queue queue_CreateQueue(int MaxElements) {
  Queue Q;

  if (MaxElements < MinQueueSize) {
    Error("CreateQueue Error: Queue size is too small.");
  }

  Q = malloc (sizeof(struct QueueRecord));
  if (Q == NULL) {
    FatalError("CreateQueue Error: Unable to allocate more memory.");
  }

  Q->Array = malloc( sizeof(ELEMENT_TYPE) * MaxElements );
  if (Q->Array == NULL) {
    FatalError("CreateQueue Error: Unable to allocate more memory.");
  }

  Q->Capacity = MaxElements;
  queue_MakeEmpty(Q);

  return Q;
}

/**
 * Empties all of the contents from the queue.
 */
void queue_MakeEmpty(Queue Q) {

  Q->Size = 0;
  Q->Front = 1;
  Q->Rear = 0;

}

/**
 * Frees the memeory for the passed queue.
 */
void queue_DisposeQueue(Queue Q) {
  if (Q != NULL) {
    free(Q->Array);
    free(Q);
  }
}

/**
 * A helper function used by other functions.
 */
static int Succ(int Value, Queue Q) {
  if (++Value == Q->Capacity) {
    Value = 0;
  }
  return Value;
}

/**
 * Adds the passed element to the passed queue.
 */
void queue_Enqueue(ELEMENT_TYPE X, Queue Q) {

  if (queue_IsFull(Q)) {
    Error("Enqueue Error: The queue is full.");
  } else {
    Q->Size++;
    Q->Rear = Succ(Q->Rear, Q);
    Q->Array[Q->Rear] = X;
  }

}

/**
 * Returns the value at the front of the queue,
 * but does not remove it.
 */
ELEMENT_TYPE queue_Front(Queue Q) {

  if (!queue_IsEmpty(Q)) {
    return Q->Array[Q->Front];
  }
  Error("Front Error: The queue is empty.");

  /* Return value to avoid warnings from the compiler */
  return 0;

}

/**
 * Removes the element at the front of the queue,
 * but does not return it.
 */
void queue_Dequeue(Queue Q) {

  if (queue_IsEmpty(Q)) {
    Error("Dequeue Error: The queue is empty.");
  } else {
    Q->Size--;
    Q->Front = Succ(Q->Front, Q);
  }

}

/**
 * Removes and returns the value at the front
 * of the queue.
 */
ELEMENT_TYPE queue_FrontAndDequeue(Queue Q) {

	ELEMENT_TYPE X = 0;

  if (queue_IsEmpty(Q)) {
    Error("FrontAndDequeue Error: The queue is empty.");
  } else {
    Q->Size--;
    X = Q->Array[Q->Front];
    Q->Front = Succ(Q->Front, Q);
  }
  return X;

}



int queue_size(Queue q) {

	return q->Size;
}
